package a_star;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Set;

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;

public class Map {

	private int rows;
	private int columns;
	private Node[][] elements;
	private Node start;
	private Node goal;
	private Canvas canvas;
	private float imageW;
	private float imageH;
	private int imageSplit;
	private int mode = 1; // mode 1 for blind

	private List<Node> opened = new ArrayList<Node>();
	private List<Node> closed = new ArrayList<Node>();
	private List<Node> bestList = new ArrayList<Node>();

	public List<Node> getBestList() {
		return bestList;
	}

	public void setBestList(List<Node> bestList) {
		this.bestList = bestList;
	}

	public List<Node> getClosed() {
		return closed;
	}

	public void setClosed(List<Node> closed) {
		this.closed = closed;
	}

	public Node[][] getElements() {
		return elements;
	}

	public void setElements(Node[][] elements) {
		this.elements = elements;
	}

	public Node getGoal() {
		return goal;
	}

	public void setGoal(Node goal) {
		this.goal = goal;
	}

	public List<Node> getOpened() {
		return opened;
	}

	public void setOpened(List<Node> opened) {
		this.opened = opened;
	}

	public void setColumns(int columns) {
		this.columns = columns;
	}

	public void setRows(int rows) {
		this.rows = rows;
	}

	public Map(int rows, int columns, int mode) {

		this.rows = rows;
		this.columns = columns;
		elements = new Node[rows][columns];
		this.mode = mode;
	}


	public int getMode() {
		return mode;
	}

	public void setMode(int mode) {
		this.mode = mode;
	}

	// map setting
	private void setupMap() {
		//setStartAndGoal(); 
		generateAdjacenies();
	}

	public int getRows() {

		return rows;
	}

	public int getColumns() {

		return columns;
	}

	// 시작점 끝점 랜덤 그리기 (추후 뺄 예정)
	private void setStartAndGoal() {
		start = elements[20][26];
		start.setStart(true);
		Random random = new Random();
		int goalX = 0, goalY = 0;
		while (goalX == 0 && goalY == 0) {
			goalX = random.nextInt(rows);
			goalY = random.nextInt(columns);
		}
		goal = elements[40][5];
		goal.setEnd(true);
	}
	
	public void setStartPoint(float x, float y){
		int nodeX = (int) (x/gapWidth);
		int nodeY = (int) (y/gapHeight);
		
/*		int nodeX = (int) (x);
		int nodeY = (int) (y);*/
		
		start = elements[nodeX][nodeY];
		start.setStart(true);
	}
	
	public void setGoalPoint(float x, float y){
		int nodeX = (int) (x/gapWidth);
		int nodeY = (int) (y/gapHeight);	
		
		/*int nodeX = (int) (x);
		int nodeY = (int) (y);*/
		
		goal = elements[nodeX][nodeY];
		goal.setEnd(true);
	}

	private void generateAdjacenies() {

		for (int i = 0; i < rows; i++) {
			for (int j = 0; j < columns; j++) {
				elements[i][j].calculateAdjacencies();
			}
		}
	}
	private float gapWidth;
	private float gapHeight;
	public void setCanvas(Canvas canvas){
		this.canvas = canvas;
	}

	// Map Info !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	public void createNode(float w, float h, int split) {
		imageW = w;
		imageH = h;
		imageSplit = split;
		gapWidth =  w/split;
		gapHeight = h/split;

		for (int i = 0; i < rows; i++) {
			for (int j = 0; j < columns; j++) {	
				elements[i][j] = new Node(i, j, this);

				// pixel value set
				elements[i][j].setX_p(i*gapWidth);
				elements[i][j].setY_p(j*gapHeight);

				// obstacle set
				//elements[i][j].setObstacle(false);

			}
		}

		// obstacle set
		// screen 
		
		for(int i = 0; i < columns; i++){
			elements[48][i].setObstacle(true);
			elements[49][i].setObstacle(true);
		}

		// chair
		for(int i=0; i<38; i++){
			for(int j=0; j<5; j++){
				elements[i][j].setObstacle(true);
			}
		}
		for(int i=0; i<10; i++){
			for(int j=5; j<columns; j++){
				elements[i][j].setObstacle(true);
			}
		}
		for(int i=10; i<13; i++){
			for(int j=4; j<10; j++){
				elements[i][j].setObstacle(true);
			}
		}
		for(int i=10; i<38; i++){
			for(int j=45; j<rows; j++){
				elements[i][j].setObstacle(true);
			}
		}
		for(int i=10; i<14; i++){
			for(int j=40; j<46; j++){
				elements[i][j].setObstacle(true);
			}
		}
		// 대각선
		elements[10][36].setObstacle(true);
		for(int i=10; i<12; i++){
			elements[i][37].setObstacle(true);
		}
		for(int i=10; i<13; i++){
			elements[i][38].setObstacle(true);
		}
		for(int i=10; i<14; i++){
			elements[i][39].setObstacle(true);
		}
		for(int i=10; i<15; i++){
			elements[i][40].setObstacle(true);
		}
		for(int i=13; i<21; i++){
			elements[i][41].setObstacle(true);
		}
		for(int i=13; i<28; i++){
			elements[i][42].setObstacle(true);
		}
		for(int i=13; i<33; i++){
			elements[i][43].setObstacle(true);
		}
		for(int i=13; i<37; i++){
			elements[i][44].setObstacle(true);
		}
		
		for(int i=13;i<38;i++){
			elements[i][5].setObstacle(true);
		}
		for(int i=13;i<32;i++){
			elements[i][6].setObstacle(true);
		}
		for(int i=13;i<26;i++){
			elements[i][7].setObstacle(true);
		}
		for(int i=13;i<20;i++){
			elements[i][8].setObstacle(true);
		}
		elements[10][10].setObstacle(true);elements[11][10].setObstacle(true);elements[10][11].setObstacle(true);
		elements[11][11].setObstacle(true);elements[10][12].setObstacle(true);
		setupMap();
	}

	public Node getNode(int x, int y) {

		return elements[x][y];
	}

	public void setNode(Node node) {

		elements[node.getX()][node.getY()] = node;
	}



	/***************여기부터 맵 그리기 파트***************/

	public void draw() {

		for (int i = 0; i < rows; i++) {
			for (int j = 0; j < columns; j++) {
				Node node = elements[i][j];
				drawMap(node, canvas);
			}

		}
	}

	private void drawMap(Node node, Canvas canvas) {

		Paint pnt = new Paint();
		int x = node.getX();
		int y = node.getY();

		if (node.isStart()) {
			pnt.setColor(Color.BLACK);
			canvas.drawRect(node.getX_p(), node.getY_p(), node.getX_p()+gapWidth, node.getY_p()+gapHeight, pnt);
			return;
		}

		if (node.isEnd()) {
			pnt.setColor(Color.BLACK);
			canvas.drawRect(node.getX_p(), node.getY_p(), node.getX_p()+gapWidth, node.getY_p()+gapHeight, pnt);
			return;
		}

		if(node.isObstacle()){
			pnt.setColor(Color.RED);
			//canvas.drawRect(node.getX_p(), node.getY_p(), node.getX_p()+gapWidth, node.getY_p()+gapHeight, pnt);
			return;
		}

		if (bestList.contains(node)) {
			pnt.setColor(Color.CYAN);
			canvas.drawRect(node.getX_p(), node.getY_p(), node.getX_p()+gapWidth, node.getY_p()+gapHeight, pnt);
			return;
		}
	}
	
	public void drawPoint(float x, float y) {
		/*int nodeX = (int) (x/gapWidth);
		int nodeY = (int) (y/gapHeight);*/
		
		int nodeX = (int) (x);
		int nodeY = (int) (y);
		Node node = getNode(nodeX, nodeY);
		Paint pnt = new Paint();
		pnt.setColor(Color.RED);
		canvas.drawRect(node.getX_p(), node.getY_p(), node.getX_p()+gapWidth, node.getY_p()+gapHeight, pnt);

	}
	
	public void drawBeacon(int x, int y, double d) {
		//int nodeX = (int) (x/gapWidth);
		//int nodeY = (int) (y/gapHeight);
		Node node = getNode(x, y);
		Paint pnt = new Paint();
		pnt.setColor(Color.YELLOW);
		canvas.drawRect(node.getX_p(), node.getY_p(), node.getX_p()+gapWidth, node.getY_p()+gapHeight, pnt);
		d = d * 58.17 * 0.7;
		pnt.setStyle(Paint.Style.STROKE);
	    pnt.setColor(Color.BLACK);
	    canvas.drawCircle(node.getX_p(), node.getY_p(),(float)d,pnt);

	}
	/***************여기까지***************/


	// Fine Best Path
	// elements[0][0] 이 시작점!
	public void findBestPath() { // 찾기!
		generateAdjacenies();
		Set<Node> adjacencies = start.getAdjacencies();
		for (Node adjacency : adjacencies) {
			adjacency.setParent(start);
			if (adjacency.isStart() == false) {
				opened.add(adjacency);
			}
		}

		while (opened.size() > 0) {
			Node best = findBestPassThrough();
			opened.remove(best);
			closed.add(best);
			if (best.isEnd()) {
				System.out.println("Found Goal");
				populateBestList(goal);
				draw();
				return;
			} else {
				Set<Node> neighbors = best.getAdjacencies();
				for (Node neighbor : neighbors) {
					if (opened.contains(neighbor)) {
						Node tmpnode = new Node(neighbor.getX(),
								neighbor.getY(), this);
						tmpnode.setParent(best);
						if (tmpnode.getValueF(goal) >= neighbor
								.getValueF(goal)) {
							continue;
						}
					}

					if (closed.contains(neighbor)) {
						Node tmpnode = new Node(neighbor.getX(),
								neighbor.getY(), this);
						tmpnode.setParent(best);
						if (tmpnode.getValueF(goal) >= neighbor
								.getValueF(goal)) {
							continue;
						}
					}

					neighbor.setParent(best);

					opened.remove(neighbor);
					closed.remove(neighbor);
					opened.add(0, neighbor);
				}
			}
		}
		//System.out.println("No Path to goal");
	}

	private void populateBestList(Node node) {

		bestList.add(node);
		if (node.getParent().isStart() == false) {
			populateBestList(node.getParent());
		}

		return;
	}

	private Node findBestPassThrough() {

		Node best = null;
		for (Node node : opened) {
			if (best == null
					|| node.getValueF(goal) < best.getValueF(goal)) {
				best = node;
			}
		}

		return best;
	}

}
